package com.google.wave.extensions.robot;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import com.google.wave.api.AbstractRobot;
import com.google.wave.api.Blip;
import com.google.wave.api.BlipContent;
import com.google.wave.api.BlipContentRefs;
import com.google.wave.api.Context;
import com.google.wave.api.Element;
import com.google.wave.api.ElementType;
import com.google.wave.api.Gadget;
import com.google.wave.api.Restriction;
import com.google.wave.api.Wavelet;
import com.google.wave.api.event.BlipSubmittedEvent;
import com.google.wave.api.event.FormButtonClickedEvent;
import com.google.wave.api.event.GadgetStateChangedEvent;
import com.google.wave.api.event.WaveletSelfAddedEvent;
import com.google.wave.api.event.WaveletSelfRemovedEvent;

public class RobotServlet extends AbstractRobot {
	public static final Logger LOG = Logger.getLogger(RobotServlet.class.getName());
	private static final long serialVersionUID = 1L;
	public static final String nameProp = "name";
	public static final String valueProp = "value";
	public static final String checkName = "checkboxDelete";
	public static final String checkValueTrue = "true";
	public static final String checkValueFalse = "false";
	public static final String buttonDeleteName = "buttonDelete";
	public static final String buttonDeleteValue = "Delete";
	public static final String buttonDeleteSelectedName = "buttonDeleteSelected";
	public static final String buttonDeleteSelectedValue = "Delete Selected";
	public static final String utilGadgetURL = "http://utilrobot.googlecode.com/svn/trunk/utilgadget.xml";
	static final String deleteSelectedGadgetURL = "http://utilrobot.googlecode.com/svn/trunk/deleteSelected.xml";
	public static final String urlProt = "url";
	static final String deleteOption = "deleteOption";
	static final String deleteSelectedOption = "deleteSelectedOption";
	static final String addElementsOption = "addElementsOption";
	static final String checkboxDelete = "checkboxDelete";
	private List<String> queue;
	String [] reservedAnnotations = {"style/backgroundColor",
									"style/color",
									"style/fontFamily",
									"style/fontSize",
									"style/fontStyle",
									"style/fontWeight",
									"style/textDecoration",
									"style/verticalAlign",
									"link/manual",
									"link/wave",
									"lang",
									"conv/title"
									};
	
	private Element deleteElement(Blip blip, ElementType type, Restriction...restrictions){
		BlipContentRefs content = blip.first(type, restrictions);
		BlipContent blipContent = content.value();
		Element el = null;
		if(blipContent != null){
			el = blipContent.asElement();
			content.delete();
		}
		return el;
	}
	
	/*private Element getUtilGadget(){
		Element gadget = new Gadget(utilGadgetURL);
		return gadget;
	}*/
	
	private Element getCheck(){
		Element el = new Element(ElementType.CHECK);
		el.setProperty(nameProp, checkName);
		return el;
	}
	
	private Element getDeleteButton(){
		Element el = new Element(ElementType.BUTTON);
		el.setProperty(nameProp, buttonDeleteName);
		el.setProperty(valueProp, buttonDeleteValue);
		return el;
	}
	
	/*private Element getDeleteSelectedButton(){
		Element el = new Element(ElementType.BUTTON);
		el.setProperty(nameProp, buttonDeleteSelectedName);
		el.setProperty(valueProp, buttonDeleteSelectedValue);
		return el;
	}*/
	
	private void removeTailingLines(Blip blip) {
		int sz = blip.length();
		for(int i = sz - 1; i > 0; i--) {
			BlipContentRefs blipContentRefs = blip.at(i);
			BlipContent blipContent = blipContentRefs.value();
			Element element = blipContent.asElement();
			if(element != null && element.getType() == ElementType.LINE) {
				blipContentRefs.delete();
			}
			else {
				return;
			}
		}
	}
	
	private void updateGadget(Blip blip, String url, BlipContent gadget){
		deleteElement(blip, ElementType.GADGET, Restriction.of("url", url));
		BlipContentRefs refs = blip.at(blip.length()-1);
		if(gadget == null || gadget.asElement() == null){
			refs.insertAfter(new Gadget(url));
			//blip.append(new Gadget(url));
		}
		else{
			refs.insertAfter(gadget);
			//blip.append(gadget);
		}
		//refs.insertAfter(getDeleteButton());
	}
	
	void clearAnnotations(BlipContentRefs refs){
		for(String annotation: reservedAnnotations){
			refs.clearAnnotation(annotation);
		}
	}
	
	void updateRootBlip(Blip blip, Map<String, String> props){
		deleteElement(blip, ElementType.BUTTON, Restriction.of(nameProp, buttonDeleteSelectedName));
		deleteElement(blip, ElementType.GADGET, Restriction.of("url", utilGadgetURL));
		deleteElement(blip, ElementType.CHECK,	Restriction.of(nameProp, checkName));
		deleteElement(blip, ElementType.BUTTON, Restriction.of(nameProp, buttonDeleteName));
		//updateGadget(blip, deleteSelectedGadgetURL, null);
		//deleteElement(blip, ElementType.BUTTON, Restriction.of(nameProp, buttonDeleteSelectedName));

		deleteElement(blip, ElementType.GADGET, Restriction.of("url", deleteSelectedGadgetURL));
		removeTailingLines(blip);
		BlipContentRefs refs = blip.at(blip.length()-1);
		Gadget gadget = new Gadget(deleteSelectedGadgetURL);
		if(props != null){
			Set<String> keys = props.keySet();
			for(String key: keys){
				gadget.setProperty(key, props.get(key));
			}
		}
		refs.insertAfter(gadget);
			//BlipContent content = blip.first(ElementType.GADGET, Restriction.of("url", deleteSelectedGadgetURL)).value();
			//updateGadget(blip, deleteSelectedGadgetURL, content);
		/*removeTailingLines(blip);
		blip.append("\n");
		blip.append(getDeleteSelectedButton());*/
	}
	
	void updateNotRootBlip(Blip blip){
		LOG.info("updateNotRootBlip");
		deleteElement(blip, ElementType.BUTTON, Restriction.of(nameProp, buttonDeleteSelectedName));
		deleteElement(blip, ElementType.GADGET, Restriction.of("url", utilGadgetURL));
		removeTailingLines(blip);

		boolean isElementLast = false;
		
		if(blip.length() < 1){
			BlipContentRefs refs= blip.at(blip.length()-1);
			BlipContent content= refs.value();
			Element last = content.asElement();
			if(last != null){
				if(last.isFormElement()){
					String name = last.getProperty(nameProp);
					if(name.equals(buttonDeleteName)){
						isElementLast = true;
					}
				}
			}
		}
		if(!isElementLast){
			deleteElement(blip, ElementType.CHECK,	Restriction.of(nameProp, checkName));
			deleteElement(blip, ElementType.BUTTON, Restriction.of(nameProp, buttonDeleteName));
			removeTailingLines(blip);
			//BlipContent content = blip.first(ElementType.GADGET, Restriction.of("url", utilGadgetURL)).value();
			
			//updateGadget(blip, utilGadgetURL, content);
			blip.append("\n\n");
			
			blip.append(getCheck());
			blip.append(getDeleteButton());
		}
		
		
		
		/*deleteElement(blip, ElementType.GADGET, Restriction.of("url", utilGadgetURL));
		removeTailingLines(blip);
		blip.append("\n");*/
		/*blip.append(getCheck());
		blip.append(getDeleteButton());*/
		/*if(content != null){
			Element element = content.asElement();
			element.setProperty("deleteSelectedOption", "0");
			blip.append(element);
		}
		else{
			blip.append(getUtilGadget());
		}*/
	}
	
	private void updateElements(Blip blip, boolean needCheck){
		//LOG.info("updateElements");
		if(blip == null) return;
		if(blip.isRoot()){
			//LOG.info("update root");
			/*blip.reply().append(blip.getCreator());
			blip.reply().append(blip.getContributors().toString());*/
			deleteElement(blip, ElementType.BUTTON, Restriction.of(nameProp, buttonDeleteSelectedName));
			deleteElement(blip, ElementType.GADGET, Restriction.of("url", utilGadgetURL));
			deleteElement(blip, ElementType.CHECK,	Restriction.of(nameProp, checkName));
			deleteElement(blip, ElementType.BUTTON, Restriction.of(nameProp, buttonDeleteName));
			//updateGadget(blip, deleteSelectedGadgetURL, null);
			//deleteElement(blip, ElementType.BUTTON, Restriction.of(nameProp, buttonDeleteSelectedName));
			boolean isElementLast = false;
			if(blip.length() > 1){
				BlipContentRefs refs= blip.at(blip.length()-1);
				BlipContent content= refs.value();
				Element last = content.asElement();
				if(last != null){
					if(last.isGadget()){
						String url = last.getProperty("url");
						if(url != null && url.equals(deleteSelectedGadgetURL)){
							isElementLast = true;
						}
					}
				}
			}
			if(!isElementLast){
				Gadget gadget = (Gadget)deleteElement(blip, ElementType.GADGET, Restriction.of("url", deleteSelectedGadgetURL));
				removeTailingLines(blip);
				BlipContentRefs refs = blip.at(blip.length()-1);
				if(gadget == null){
					gadget = new Gadget(deleteSelectedGadgetURL);
				}
				refs.insertAfter(gadget);
				//BlipContent content = blip.first(ElementType.GADGET, Restriction.of("url", deleteSelectedGadgetURL)).value();
				//updateGadget(blip, deleteSelectedGadgetURL, content);
			}
		}
		else{
			updateNotRootBlip(blip);
		}
	}
	
	private void deleteElements(Blip blip, boolean deleteRootFlag){
		if(blip == null) return;
		if(blip.isRoot() && deleteRootFlag){
			deleteElement(blip, ElementType.BUTTON, Restriction.of(nameProp, buttonDeleteSelectedName));
		}
		else{
			deleteElement(blip, ElementType.CHECK,	Restriction.of(nameProp, checkName));
			deleteElement(blip, ElementType.BUTTON, Restriction.of(nameProp, buttonDeleteName));
			deleteElement(blip, ElementType.BUTTON, Restriction.of(nameProp, buttonDeleteSelectedName));
			deleteElement(blip, ElementType.GADGET, Restriction.of("url", utilGadgetURL));
			removeTailingLines(blip);
		}
	}
	
	public void onBlipSubmitted(BlipSubmittedEvent e){
		updateElements(e.getBlip(), true);
	};
	
	@Capability(contexts = {Context.ALL})
	public void onFormButtonClicked(FormButtonClickedEvent e){
		String buttonName = e.getButtonName();
		if(buttonName.equals(buttonDeleteName)){
			Blip blip = e.getBlip();
			if(!blip.isRoot()) {
				e.getWavelet().delete(blip);
			}
			return;
		}
		if(buttonName.equals(buttonDeleteSelectedName)){
			if(queue == null){
				queue = new LinkedList<String>();
			}
			queue.clear();
			Wavelet wavelet = e.getWavelet();
			Map <String, Blip> mp = wavelet.getBlips();
			Collection<Blip> vals = mp.values();
			for(Blip val: vals){
				BlipContentRefs content = val.first(ElementType.CHECK, 
						Restriction.of(nameProp, checkName));
				BlipContent blipContent = content.value();
				if(blipContent == null) continue;
				if(blipContent.asElement().getProperty(valueProp).equals(
						checkValueTrue)){
					queue.add(val.getBlipId());
				}	
			}
			for(String q: queue){
				wavelet.delete(q);
			}
		}
	}
	
	@Capability(contexts = {Context.ALL})
	public void onGadgetStateChanged(GadgetStateChangedEvent event){
		Blip blip = event.getBlip(); 
		/*BlipContent blipContent = blip.first(ElementType.GADGET, Restriction.of("url", utilGadgetURL)).value();
		if(blipContent != null){
			Element el = blipContent.asElement();
			if(el.getProperty(deleteOption) != null){
				event.getWavelet().delete(event.getBlip());
				return;
			}
		}*/
		BlipContentRefs gadgetRefs = blip.first(ElementType.GADGET, Restriction.of("url", deleteSelectedGadgetURL));
		BlipContent blipContent = gadgetRefs.value();
		if(blipContent != null){
			Element el = blipContent.asElement();
			Map <String, String> props = el.getProperties();
			String property = props.get(deleteSelectedOption);
			if(property != null){
				if(!property.equals("1")){
					return;
				}
				props.put(deleteSelectedOption, "0");
				//el.setProperty(deleteSelectedOption, "0");
				if(queue == null){
					queue = new LinkedList<String>();
				}
				queue.clear();
				Wavelet wavelet = event.getWavelet();
				Map <String, Blip> mp = wavelet.getBlips();
				Collection<Blip> vals = mp.values();
				for(Blip val: vals){
					BlipContentRefs content = val.first(ElementType.CHECK, 
							Restriction.of(nameProp, checkName));
					blipContent = content.value();
					if(blipContent == null) continue;
					if(blipContent.asElement().getProperty(valueProp).equals(
							checkValueTrue)){
						queue.add(val.getBlipId());
					}	
				}
				for(String q: queue){
					wavelet.delete(q);
				}
				updateRootBlip(blip, props);
				//gadgetRefs.delete();
				
				
				/*if(queue == null){
					queue = new LinkedList<String>();
				}
				queue.clear();
				Wavelet wavelet = event.getWavelet();
				Map <String, Blip> mp = wavelet.getBlips();
				Collection<Blip> vals = mp.values();
				for(Blip val: vals){
					BlipContentRefs content = val.first(ElementType.GADGET, Restriction.of("url", utilGadgetURL));
					blipContent = content.value();
					if(blipContent == null) continue;
					String checkProp = blipContent.asElement().getProperty(checkboxDelete);
					if(checkProp != null){
						if(checkProp.equals("true")){
							queue.add(val.getBlipId());
						}
					}	
				}
				for(String q: queue){
					wavelet.delete(q);
				}*/
				return;
			}
			property = props.get(addElementsOption);
			if(property != null){
				Wavelet wavelet = event.getWavelet();
				Map <String, Blip> mp = wavelet.getBlips();
				/*Set<String> set = mp.keySet();
				for(String id: set) {
					updateElements(wavelet.getBlip(id));
				}//*/
				// TODO: concurrent modification exception
				
				Collection<Blip> blips = mp.values();
				for(Blip b: blips){
					if(property.equals("1")){
						updateElements(b, false);
						
					}
					else{
						deleteElements(b, false);
					}
				}
			}			
		}	
	}	

	@Capability(contexts = {Context.ALL})
	public void onWaveletSelfRemoved(WaveletSelfRemovedEvent event) {
		Wavelet wavelet = event.getWavelet();
		wavelet.getRootBlip().reply().append("Bye");
		/*Map <String, Blip> mp = wavelet.getBlips();
		Set<String> set = mp.keySet();
		for(String id: set) {
			deleteElements(wavelet.getBlip(id), true);
		}*/
	}
	
	@Capability(contexts = {Context.ALL})
	public void onWaveletSelfAdded(WaveletSelfAddedEvent event){
		Wavelet wavelet = event.getWavelet();
		Map <String, Blip> mp = wavelet.getBlips();
		/*Set<String> set = mp.keySet();
		for(String id: set) {
			updateElements(wavelet.getBlip(id));
		}//*/
		// TODO: concurrent modification exception
		
		Collection<Blip> blips = mp.values();
		for(Blip blip: blips){
			updateElements(blip, true);
		}
	}
	
	protected String getRobotName() {
		return "Robot";
	}
}